#include "Odbc.h"
#include "OdbcResult.h"

#include <memory>

#ifdef DBMS_USE_ODBC

#if defined( WIN32 ) || defined ( WIN64 )

#pragma  warning(disable:4996)

LIBDBC_BEGIN_NAMESPACE

SQLHENV Odbc::_hEnv = SQL_NULL_HENV;
int Odbc::_nInstances = 0;

Odbc::Odbc()
{
	_hDbc = SQL_NULL_HDBC;
	if (_nInstances == 0)
	{
		_hEnv = SQL_NULL_HENV;
		SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &_hEnv);
		SQLSetEnvAttr(_hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
	}
	_hDbc = SQL_NULL_HDBC;

	_host = _database = _user = _pwd = 0;
	_port = 0;

	_nInstances++;
}


Odbc::~Odbc()
{
	close();

	if (_nInstances == 0)
	{
		SQLFreeHandle(SQL_HANDLE_ENV, _hEnv);
		_hEnv = SQL_NULL_HENV;
	}
}

bool Odbc::_connect(const char* host, const char* database, const char* user, const char* pwd, int port)
{
	bool bConnected = connect(host, user, pwd, port);
	use(database);
	return bConnected;
}

bool Odbc::_connect(const char* host, const char* user, const char* pwd, int port)
{
	bool bOK = true;

	_storeConnectionInfo(host, user, pwd, port);

	SQLAllocHandle(SQL_HANDLE_DBC, _hEnv, &_hDbc);
	SQLRETURN rc = SQLConnect(_hDbc, (unsigned char*)host, SQL_NTS, NULL, 0, NULL, 0);
	if (!_isSuccess(rc))
	{
		SQLFreeHandle(SQL_HANDLE_DBC, _hDbc);
		_hDbc = NULL;
		bOK = false;
	}
	return bOK;
}

void Odbc::_storeConnectionInfo(const char* host, const char* user, const char* pwd, int port)
{
	if (_host) { delete _host; _host = 0; }
	if (_user) { delete _user; _user = 0; }
	if (_pwd) { delete _pwd; _pwd = 0; }

	if (host) _host = strdup(host);
	if (user) _user = strdup(user);
	if (pwd) _pwd = strdup(pwd);
}

void Odbc::_storeDatabaseName(const char* database)
{
	if (_database) { delete _database; _database = NULL; }
	if (database) _database = strdup(database);
}

bool Odbc::_createConnectedHDBC(SQLHDBC* phdbc)
{
	bool bOK = true;
	SQLRETURN rc;
	rc = SQLAllocHandle(SQL_HANDLE_DBC, _hEnv, phdbc);
	if (_isSuccess(rc))
	{
		rc = SQLConnect(*phdbc, (unsigned char*)_host, SQL_NTS, NULL, 0, NULL, 0);
	}

	if (!_isSuccess(rc))
	{
		SQLFreeHandle(SQL_HANDLE_DBC, *phdbc);
		*phdbc = NULL;
		bOK = false;
	}

	return bOK;
}

bool Odbc::_close()
{
	bool bClosed = false;

	if (_hDbc != SQL_NULL_HDBC)
	{
		SQLRETURN disconnected = SQLDisconnect(_hDbc);
		SQLFreeHandle(SQL_HANDLE_DBC, _hDbc);
		_hDbc = SQL_NULL_HDBC;
		bClosed = _isSuccess(disconnected);
	}

	return bClosed;
}

IResultSet* Odbc::_execQuery(const char* query)
{
	OdbcResult* pRes = NULL;
	SQLRETURN rc;
	SQLHDBC hdbc;
	SQLHSTMT hstmt;         // Statement handle

	if (!_createConnectedHDBC(&hdbc))
	{
		// warning: fallback
		__debugbreak();
		hdbc = _hDbc;
	}
	//   OLD: rc = SQLAllocHandle( SQL_HANDLE_STMT, _hDbc, &hstmt);
	rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	rc = SQLPrepare(hstmt, (unsigned char*)query, SQL_NTS);
	rc = SQLExecute(hstmt);

	if (_isSuccess(rc))
	{
		pRes = new OdbcResult(hstmt, _hDbc != hdbc ? hdbc : 0);
	}

	return _createResultSet(pRes);
}

int Odbc::_execNonQuery(const char* query)
{
	int result = 0;
	SQLRETURN rc;
	SQLHSTMT hstmt;         // Statement handle
	rc = SQLAllocHandle(SQL_HANDLE_STMT, _hDbc, &hstmt);
	rc = SQLPrepare(hstmt, (unsigned char*)query, SQL_NTS);
	rc = SQLExecute(hstmt);

	if ((rc != SQL_NO_DATA) && (_isSuccess(rc)))
		result = 1;

	SQLFreeHandle(SQL_HANDLE_STMT, hstmt);

	return result;
}

void Odbc::_use(const char* database)
{
	_storeDatabaseName(database);

	/* nothing to do - an odbc-connection always has its own database */
}

void Odbc::_internalSwitchDatabase(HDBC hdbc, const char* database)
{
	/* dc nothing! */
}


bool Odbc::_isSuccess(SQLRETURN rc)
{
	bool success = (rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO);

	if (!success)
	{
		// SQLGetDiagRec( SQL_HANDLE_DBC, _hDbc, 1, 
		// TODO remember last error!
	}

	return success;
}

LIBDBC_END_NAMESPACE

#endif
#endif